/**
 * @file rtk_work.c
 * @author LokLiang (lokliang@163.com)
 * @brief
 * @version 0.1
 * @date 2023-05-01
 *
 * @copyright Copyright (c) 2023
 *
 */

#include "prv_rtk_class.h"

static rtk_work_q_t rtk_work_q_hdl_main;
rtk_work_q_t *default_rtk_work_q_hdl = &rtk_work_q_hdl_main;

static void _rtk_work_q_thread(void *arg);
static void _rtk_work_q_resume(void *arg);
static rtk_err_t _rtk_work_q_init(rtk_work_q_t *work_q_handle, struct rtk_thread_handle *thread);
static struct rtk_thread_handle *_rtk_work_get_thread_handle(rtk_work_q_t *work_q_handle);

static void _rtk_work_q_thread(void *arg)
{
    _RTK_UNUSE(arg);

    for (;;)
    {
        for (;;)
        {
            size_t sched_nest = rtk_sched_suspend();
            if (curr_thread_handle->obj_enable == 0)
            {
                rtk_thread_suspend();
                rtk_sched_resume(sched_nest);
            }
            else
            {
                rtk_sched_resume(sched_nest);
                break;
            }
        }

        rtk_tick_t timeout = k_work_q_handler(&curr_thread_handle->obj.work_q_handle);

        if ((cm_rtk->nest_sched | cm_rtk->nest_int) == 0)
        {
            rtk_thread_sleep(timeout);
        }
    }
}

static void _rtk_work_q_resume(void *arg)
{
    _rtk_list_atomic_resume(arg);
}

static rtk_err_t _rtk_work_q_init(rtk_work_q_t *work_q_handle, struct rtk_thread_handle *thread)
{
    if (k_work_q_create(&thread->obj.work_q_handle) != 0)
    {
        return RTK_NOMEM;
    }

    k_work_q_resume_regist(&thread->obj.work_q_handle, _rtk_work_q_resume, thread);

    if (work_q_handle)
    {
        work_q_handle->hdl = &thread->obj.work_q_handle;
    }

    rtk_work_q_start(work_q_handle);

    return RTK_OK;
}

/**
 * @brief 获取 work_q 对应的 thread_handle
 *
 * @param work_q_handle
 * @return struct rtk_thread_handle*
 */
static struct rtk_thread_handle *_rtk_work_get_thread_handle(rtk_work_q_t *work_q_handle)
{
    struct rtk_thread_handle *thread = (struct rtk_thread_handle *)&((uint8_t *)(work_q_handle->hdl))[-(int)&((struct rtk_thread_handle *)0)->obj.work_q_handle];
    return thread;
}

rtk_err_t rtk_work_q_init(rtk_work_q_t *work_q_handle, // 队列句柄
                          const char *name,            // 队列名
                          void *stack_base,            // 栈内存地址
                          size_t stack_size,           // 栈大小（字节）
                          rtk_prior_t priority         // 优先级（0 为最低，CONFIG_RTK_MAX_PRIORITY 为最高。若输入为负值则表示优先级为 (CONFIG_RTK_MAX_PRIORITY + 1 + priority)）
)
{
    _RTK_TRACE_ENTRY();

    rtk_err_t ret;

    do
    {
        rtk_thread_t *thead_handle = rtk_thread_init(NULL,               // rtk_thread_t *thread_handle,
                                                     name,               // const char *name,
                                                     _rtk_work_q_thread, // void (*entry)(void *arg),
                                                     0,                  // void *arg,
                                                     stack_base,         // void *stack_base,
                                                     stack_size,         // size_t stack_size,
                                                     priority,           // rtk_prior_t priority,
                                                     0);                 // u8_t slice_ticks

        if (thead_handle == NULL)
        {
            ret = RTK_NOMEM;
            break;
        }

        ret = _rtk_work_q_init(work_q_handle, thead_handle->hdl);

    } while (0);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_work_q_create(rtk_work_q_t *work_q_handle, // 队列句柄
                            const char *name,            // 队列名
                            size_t stack_size,           // 栈大小（字节）
                            rtk_prior_t priority         // 优先级（0 为最低，CONFIG_RTK_MAX_PRIORITY 为最高。若输入为负值则表示优先级为 (CONFIG_RTK_MAX_PRIORITY + 1 + priority)）
)
{
    _RTK_TRACE_ENTRY();

    rtk_err_t ret;

    do
    {
        rtk_thread_t *thread_handle = rtk_thread_create(NULL,               // rtk_thread_t *thread_handle,
                                                        name,               // const char *name,
                                                        _rtk_work_q_thread, // void (*entry)(void *arg),
                                                        NULL,               // void *arg,
                                                        stack_size,         // size_t stack_size,
                                                        priority,           // rtk_prior_t priority,
                                                        0);                 // u8_t slice_ticks

        if (thread_handle == NULL)
        {
            ret = RTK_NOMEM;
            break;
        }

        struct rtk_thread_handle *thread = thread_handle->hdl;
        thread->flag |= _RTK_THREAD_FLAG_WORKQ;

        ret = _rtk_work_q_init(work_q_handle, thread_handle->hdl);
        if (ret != RTK_OK)
        {
            rtk_thread_delete(thread_handle);
        }

    } while (0);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_work_q_delete(rtk_work_q_t *work_q_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();

    rtk_err_t ret;

    do
    {
        if (rtk_work_q_is_valid(work_q_handle) == false)
        {
            _RTK_WRN("work_q is invalid");
            ret = RTK_FAIL;
            break;
        }

        size_t sched_nest = rtk_sched_suspend();

        struct rtk_thread_handle *thread = _rtk_work_get_thread_handle(work_q_handle);
        ret = rtk_thread_delete((rtk_thread_t *)&thread);
        if (ret == RTK_OK)
        {
            k_work_q_delete(work_q_handle->hdl);
            work_q_handle->hdl = NULL;
        }
        rtk_sched_resume(sched_nest);

    } while (0);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_work_q_start(rtk_work_q_t *work_q_handle)
{
    _RTK_TRACE_ENTRY();

    rtk_err_t ret;

    if (rtk_work_q_is_valid(work_q_handle) != false)
    {
        struct rtk_thread_handle *thread = _rtk_work_get_thread_handle(work_q_handle);
        thread->obj_enable = 1;
        rtk_thread_resume((rtk_thread_t *)&thread);
        ret = RTK_OK;
    }
    else
    {
        _RTK_WRN("work_q is invalid");
        ret = RTK_FAIL;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_work_q_stop(rtk_work_q_t *work_q_handle)
{
    _RTK_TRACE_ENTRY();

    rtk_err_t ret;

    if (rtk_work_q_is_valid(work_q_handle) != false)
    {
        struct rtk_thread_handle *thread = _rtk_work_get_thread_handle(work_q_handle);
        thread->obj_enable = 0;
        rtk_thread_set_suspend((rtk_thread_t *)&thread);
        ret = RTK_OK;
    }
    else
    {
        _RTK_WRN("work_q is invalid");
        ret = RTK_FAIL;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

bool rtk_work_q_is_valid(rtk_work_q_t *work_q_handle)
{
    _RTK_TRACE_ENTRY();

    bool ret;

    if (work_q_handle == NULL || work_q_handle->hdl == NULL)
    {
        ret = false;
    }
    else
    {
        ret = k_work_q_is_valid(work_q_handle->hdl);
    }

    _RTK_TRACE_EXIT();
    return ret;
}

bool rtk_work_q_delayed_state(rtk_work_q_t *work_q_handle)
{
    _RTK_TRACE_ENTRY();

    bool ret;

    if (rtk_work_q_is_valid(work_q_handle) == false)
    {
        _RTK_WRN("work_q is invalid");
        ret = false;
    }
    else
    {
        ret = k_work_q_delayed_state(work_q_handle->hdl);
    }

    _RTK_TRACE_EXIT();
    return ret;
}

bool rtk_work_q_ready_state(rtk_work_q_t *work_q_handle)
{
    _RTK_TRACE_ENTRY();

    bool ret;

    if (rtk_work_q_is_valid(work_q_handle) == false)
    {
        _RTK_WRN("work_q is invalid");
        ret = false;
    }
    else
    {
        ret = k_work_q_ready_state(work_q_handle->hdl);
    }

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_work_create(rtk_work_t *work_handle,
                          const char *name,
                          rtk_work_fn work_route,
                          void *arg,
                          u8_t sub_prior)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    k_err_t ret = k_work_create((k_work_t *)work_handle,
                                name,
                                (k_work_fn)work_route,
                                arg,
                                sub_prior);
    _RTK_TRACE_EXIT();

    if (ret == 0)
    {
        return RTK_OK;
    }
    else
    {
        return RTK_NOMEM;
    }
}

void rtk_work_delete(rtk_work_t *work_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    k_work_delete((k_work_t *)work_handle);
    _RTK_TRACE_EXIT();
}

bool rtk_work_is_valid(rtk_work_t *work_handle)
{
    _RTK_TRACE_ENTRY();
    bool ret = k_work_is_valid((k_work_t *)work_handle);
    _RTK_TRACE_EXIT();
    return ret;
}

bool rtk_work_is_pending(rtk_work_t *work_handle)
{
    _RTK_TRACE_ENTRY();
    bool ret = k_work_is_pending((k_work_t *)work_handle);
    _RTK_TRACE_EXIT();
    return ret;
}

rtk_tick_t rtk_work_time_remain(rtk_work_t *work_handle)
{
    _RTK_TRACE_ENTRY();
    rtk_tick_t ret = k_work_time_remain((k_work_t *)work_handle);
    _RTK_TRACE_EXIT();
    return ret;
}

void rtk_work_submit(rtk_work_q_t *work_q_handle, rtk_work_t *work_handle, rtk_tick_t delay_ticks)
{
    _RTK_TRACE_ENTRY();
    if (rtk_work_q_is_valid(work_q_handle) == false)
    {
        _RTK_WRN("work_q is invalid");
    }
    else
    {
        k_work_submit(work_q_handle->hdl, (k_work_t *)work_handle, delay_ticks);
    }
    _RTK_TRACE_EXIT();
}

void rtk_work_resume(rtk_work_t *work_handle, rtk_tick_t delay_ticks)
{
    _RTK_TRACE_ENTRY();
    k_work_resume((k_work_t *)work_handle, delay_ticks);
    _RTK_TRACE_EXIT();
}

void rtk_work_suspend(rtk_work_t *work_handle)
{
    _RTK_TRACE_ENTRY();
    k_work_suspend((k_work_t *)work_handle);
    _RTK_TRACE_EXIT();
}

void rtk_work_yield(rtk_tick_t delay_ticks)
{
    _RTK_TRACE_ENTRY();
    k_work_yield(delay_ticks);
    _RTK_TRACE_EXIT();
}

void rtk_work_sleep(rtk_tick_t delay_ticks)
{
    _RTK_TRACE_ENTRY();
    k_work_sleep(delay_ticks);
    _RTK_TRACE_EXIT();
}

void rtk_work_later(rtk_tick_t delay_ticks)
{
    _RTK_TRACE_ENTRY();
    k_work_later(delay_ticks);
    _RTK_TRACE_EXIT();
}

void rtk_work_later_until(rtk_tick_t delay_ticks)
{
    _RTK_TRACE_ENTRY();
    k_work_later_until(delay_ticks);
    _RTK_TRACE_EXIT();
}

void rtk_work_later_hmsm(size_t hours, size_t minute, size_t second, size_t milli)
{
    _RTK_TRACE_ENTRY();
    k_tick_t ticks = (hours * 3600 +                             // 时
                      minute * 60 +                              // 分
                      second) *                                  // 秒
                         CONFIG_RTK_TICKS_HZ +                   // 每秒节拍数
                     (milli * CONFIG_RTK_TICKS_HZ + 500) / 1000; // 节拍数

    k_work_later(ticks);
    _RTK_TRACE_EXIT();
}

rtk_work_t *rtk_get_curr_work_handle(rtk_work_q_t *work_q_handle)
{
    _RTK_TRACE_ENTRY();

    if (rtk_work_q_is_valid(work_q_handle) == false)
    {
        _RTK_WRN("work_q is invalid");
    }

    rtk_work_t *ret = (rtk_work_t *)k_get_curr_work_handle(work_q_handle->hdl);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_work_mbox_create(rtk_work_t *work_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    k_err_t ret = k_work_mbox_create((k_work_t *)work_handle);
    _RTK_TRACE_EXIT();

    if (ret == 0)
    {
        return RTK_OK;
    }
    else
    {
        return RTK_NOMEM;
    }
}

void rtk_work_mbox_delete(rtk_work_t *work_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    k_work_mbox_delete((k_work_t *)work_handle);
    _RTK_TRACE_EXIT();
}

void *rtk_work_mbox_alloc(rtk_work_t *work_handle, size_t size)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    void *ret = k_work_mbox_alloc((k_work_t *)work_handle, size);
    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_work_mbox_cancel(void *mbox)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();

    rtk_err_t ret;

    if (k_work_mbox_cancel(mbox) == 0)
    {
        ret = RTK_OK;
    }
    else
    {
        ret = RTK_E_PARAM;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_work_mbox_submit(void *mbox)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();

    rtk_err_t ret;

    if (k_work_mbox_submit(mbox) == 0)
    {
        ret = RTK_OK;
    }
    else
    {
        ret = RTK_E_PARAM;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

void *rtk_work_mbox_take(void)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    void *ret = k_work_mbox_take();
    _RTK_TRACE_EXIT();
    return ret;
}

void *rtk_work_mbox_peek(void)
{
    _RTK_TRACE_ENTRY();
    void *ret = k_work_mbox_peek();
    _RTK_TRACE_EXIT();
    return ret;
}

void rtk_work_mbox_clr(void)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    k_work_mbox_clr();
    _RTK_TRACE_EXIT();
}
